#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>   //杂项设备头文件
#include <linux/fs.h>           //文件操作集
#include <linux/of.h>           //设备树操作函数操作集
#include <linux/uaccess.h>       //copy_to_user函数的文件

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
//自定义文件
#include "oledfont.h"
//oled寄存器地址
#define OLED_CMD    0x00 //OLED写命令
#define OLED_DATA   0x40 //OLED写数据

#define Max_Column  128 //限制oled最大值

//static struct device_node *oled_device_node;//获取设备树节点
static struct i2c_client *oled_client;      //用于获取从设备元器件的地址

static void oled_write_reg(u8 reg_addr, u8 data, u8 len);

// //IIC从用户控件接收的数据格，获取用户层数据
// struct display {　　　　
//     int  x;
//     int  y;
//     char *buf;
// };

// static struct display dis_format;
// static int oled_read_reg(u8 reg_addr);

// static int oled_read_reg(u8 reg_addr)
// {
//     u8 data;
//     struct i2c_msg msgs[] = {
//     [0] = {
//         .addr = oled_client->addr,
//         .flags = 0,
//         .len = sizeof(reg_addr),
//         .buf = &reg_addr,
//         },

//     [1] = {
//         .addr = oled_client->addr,
//         .flags = 1,
//         .len = sizeof(data),
//         .buf = &data,
//         },
//     };
//     i2c_transfer(oled_client->adapter, msgs, 2);//实现对 I2C 设备寄存器进行读写操作
 
//     return data;
// }

//向寄存器写数据函数
static void oled_write_reg(u8 reg_addr, u8 data, u8 len)
{
    u8 buff[256];
    struct i2c_msg msgs[] = {
    [0] = {
        .addr = oled_client->addr,
        .flags = 0,
        .len = len + 1,
        .buf = buff,
        }
    };

    buff[0] = reg_addr;
    memcpy(&buff[1], &data, len);//拼接buff可以在函数开始前进行
    i2c_transfer(oled_client->adapter, msgs, 1);//能够对 I2C 设备寄存器进行读写操作参数1表述获取一次从地址数据对其寄存器进行1次的读写
}

/********************对oled初始化操作*******************/
//对oled进行初始化
void oled_init(void)
{
    u8 i;
    u8 data[] ={ 
            0xae,0X00,0X10,0x40,0X81,0XCF,0xff,0xa1,0xa4,
            0xA6,0xc8,0xa8,0x3F,0xd5,0x80,0xd3,0x00,0XDA,
            0X12,0x8d,0x14,0xdb,0x40,0X20,0X02,0xd9,0xf1,0xaf };

    for(i=0; i<sizeof(data); i++)
    {
       oled_write_reg(OLED_CMD, data[i], 1);
    }

}

//oled清屏函数
void oled_clear(void)
{  
    u8 i, n;
    for(i=0; i<8; i++) 
    {  
       oled_write_reg(OLED_CMD, 0xb0 + i, 1);      //设置页地址（0~7）
       oled_write_reg(OLED_CMD, 0x00,   1);        //设置显示位置—列低地址
       oled_write_reg(OLED_CMD, 0x10,   1);        //设置显示位置—列高地址

       for(n=0; n<128; n++)
       {
           oled_write_reg(OLED_DATA, 0x00, 1);
       }
    }
}

//定位oled起始位置描绘内容函数
void oled_set_pos(u8 x, u8 y)
{  
    oled_write_reg(OLED_CMD, 0xb0 + y, 1);
    oled_write_reg(OLED_CMD, ((x & 0xf0) >> 4) | 0x10, 1);
    oled_write_reg(OLED_CMD, x & 0x0f, 1);
}

//oled显示函数字符函数
void oled_showchar(u8 x, u8 y, u8 chr)
{     
    u8 c=0, i=0;
    c = chr - ' ';                    
    if(x > Max_Column-1)
    {
       x = 0;
       y = y + 2;
    }    
    oled_set_pos(x, y);
    for(i=0; i<8; i++)
    {
       oled_write_reg(OLED_DATA, F8X16[c*16+i], 1);
    }
    oled_set_pos(x,y+1);
    for(i=0; i<8; i++)
    {
       oled_write_reg(OLED_DATA, F8X16[c*16+i+8], 1);  
    }
}

//oled显示字符串函数
void oled_showstring(u8 x, u8 y, u8 *chr)
{
    unsigned char j=0;
    while(chr[j] != '\0')
    {     
       oled_showchar(x, y, chr[j]);
       x += 8;
       if(x > 120)
       {
           x = 0;
           y += 2;
       }
       j++;
    }
}
//===========================================//

/*************file_ops结构体中成员****************/
static int oled_open(struct inode *inode, struct file *filp)
{
    oled_init();
    oled_clear();
    return 0;
}

static ssize_t oled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *off)
{
    int ret;
    u8 buff[4];
    ret = copy_from_user(buff,buf,cnt);
    // printk("dis_format.x = %c \r\n",buff[0]);　　 　//调试打印，用于观察从用户控件获取的数据是否正确，可删除
    // printk("dis_format.y = %c \r\n",buff[1]);
    // printk("dis_format.buf = %s \r\n",buff[2]);
    oled_showstring(buff[0],buff[1],&buff[2]);
    return 0;
}

static int oled_release(struct inode *inode, struct file *filp)
{
    return 0;
}


struct file_operations oled_fops={
    .owner = THIS_MODULE,
    .open  = oled_open,
    .write = oled_write,
    .release = oled_release,
};

/*描述杂项设备的结构体*/
struct miscdevice oled_misc={
    .minor = MISC_DYNAMIC_MINOR, //
    .name  ="oled_misc",         //设备节点的名称查看dev
    .fops  =&oled_fops
};

int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int misc_reg;
    printk("i2c_probe!\n");
    //往地址为 0x80 的寄存器里面写入数据 0x4b
    //oled_write_reg(0x80, 0x4b, 1);
    //读出 0x80 寄存器的值
    // ret = oled_read_reg(0x80);
    //打印 0x80 寄存器的值
 //   printk("ret is %#x\n", ret);
     misc_reg = misc_register(&oled_misc);//注册杂项设备，自动生成设备节点
    if(misc_reg<0)
    {
        printk("misc register is error\n");
        return -1;
    }
    printk("misc register is ok\n");
    oled_client = client; //申请i2c的
     printk("oled_init\n");
    oled_init();
    oled_clear();
    oled_showstring(0,0,"hello world!");
    return 0;
}

/* i2c 驱动的 remove 函数 */
int oled_remove(struct i2c_client *i2c_client)
{   
    printk("This is oled_remove\n");
    misc_deregister(&oled_misc);
    oled_clear();
    return 0;
}

//与设备树的 compatible 匹配
static const struct of_device_id oled_id[] = {
{.compatible = "edt,edt-oled", 0},
{.compatible = "edt,edt-oled", 0},
{.compatible = "edt,edt-oled", 0},
{}};

// 无设备树的时候匹配 ID 表
static const struct i2c_device_id oled_id_ts[] = {
{"edt,edt-oled", 0},
{}};

//定义一个 i2c_driver 的结构体
static struct i2c_driver oled_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "oled_test",
        .of_match_table = oled_id,// 采用设备树的时候驱动使用的匹配表
    },
    .probe = oled_probe,
    .remove = oled_remove,
    .id_table = oled_id_ts
};

/* 驱动入口函数 */
static int oled_driver_init(void)
{
    int ret=0;
    //注册 i2c_driver
    ret = i2c_add_driver(&oled_driver);//
    if (ret < 0)
    {
        printk(" i2c_add_driver is error \n");
        return ret;
    }
    printk("This is oled_driver_init\n");
    return ret;
}

/* 驱动出口函数 */
static void oled_driver_exit(void)
{
    printk("This is oled_driver_exit\n");
    i2c_del_driver(&oled_driver);
}

module_init(oled_driver_init);
module_exit(oled_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CLING");